package com.service.Authorization.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.service.Authorization.dao.AuthorizationUserDao;
import com.service.Authorization.dto.AuthorizationDto;
import com.service.Authorization.dto.FindAccountAndPasswordDto;
import com.service.Authorization.dto.UserCipherInfoDto;
import com.service.Authorization.entity.*;
import com.service.Authorization.service.*;
import com.service.config.constant.ApiUrlConstants;
import com.service.config.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.service.config.constant.ApiUrlConstants.URL_FIRST;
import static com.service.config.constant.Constant.*;
import static com.service.config.utils.CharUtil.getRandomString;
import static com.service.config.utils.DateUtil.getAddDate;
import static com.service.config.utils.DateUtil.getDatePoor;
import static com.service.config.utils.DesUtil.decodeToString;


/**
 * 授权终端用户认证表(AuthorizationUser)表服务实现类
 *
 * @author makejava
 * @since 2020-08-21 13:57:44
 */
@Slf4j
@Service("authorizationUserService")
public class AuthorizationUserServiceImpl implements AuthorizationUserService , BaseService {
    private static final Logger LOG = LoggerFactory.getLogger(AuthorizationUserServiceImpl.class);
    @Resource
    private AuthorizationUserDao authorizationUserDao;
    @Resource
    RestTemplate restTemplate;
    @Resource
    RedisUtil redisUtil;
    /**
     * 用户档案service
     */
    @Resource
    private UserArchivesInfoService userArchivesInfoService;
    /**
     * 有密码认证服务
     */
    @Resource
    private UserCipherInfoService userCipherInfoService;
    /**
     * 用户设备信息服务
     */
    @Resource
    private UserDeviceInfoService userDeviceInfoService;
    /**
     * 用户上网日志服务
     */
    @Resource
    private UserLogInfoService userLogInfoService;
    /**
     * 服务对象
     */
    @Resource
    private ManageInfoService userInfoService;
    @Resource
    private ManageProgrammeInfoService manageProgrammeInfoService;
    @Resource
    private DeviceUserViewService deviceUserViewService;

    @Value("${spring.expiretime}")
    private String expiretime ;
    @Value("${spring.hours}")
    private boolean hours ;
    @Resource
    private PortalUserService portalUserService;
    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public AuthorizationUser queryById(String id) {
        return this.authorizationUserDao.queryById(id);
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    @Override
    public List<AuthorizationUser> queryAllByLimit(int offset, int limit) {
        return this.authorizationUserDao.queryAllByLimit(offset, limit);
    }

    /**
     * 新增数据
     *
     * @param authorizationUser 实例对象
     * @return 实例对象
     */
    @Override
    public AuthorizationUser insert(AuthorizationUser authorizationUser) {
        this.authorizationUserDao.insert(authorizationUser);
        return authorizationUser;
    }

    /**
     * 修改数据
     *
     * @param authorizationUser 实例对象
     * @return 实例对象
     */
    @Override
    public AuthorizationUser update(AuthorizationUser authorizationUser) {
        this.authorizationUserDao.update(authorizationUser);
        return this.queryById(authorizationUser.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(String id) {
        return this.authorizationUserDao.deleteById(id) > 0;
    }

    /**
     * 授权终端用户
     * @param authorizationVo
     * @throws Exception
     * @return
     */
    @Override
    @Transactional
    public Result setAuthorization(AuthorizationDto authorizationVo) throws Exception {
        if (authorizationVo.getPassword() != null && !"".equals(authorizationVo.getPassword())) {
            // 账号和密码都存在 则是有密码方案
            if (redisUtil.redisLikeById(authorizationVo.getPassword()).size() > 0  ){
                return isPasswordAuth(authorizationVo);
            }
            return Result.failure(PASSWORD_NO_EXPIRE,PASSWORD_NO_EXPIRE_DESC); // 密码已失效
        }else {//无密码方案
            return noPasswordAuth(authorizationVo);
        }
    }

    /**
     *  账号和密码都存在 则是有密码方案
     * @param authorizationVo
     * @return
     * @throws Exception
     */
    private Result isPasswordAuth(AuthorizationDto authorizationVo) throws Exception {
        UserCipherInfo userCipherInfo = userCipherInfoService.queryByPassword(authorizationVo.getPassword());
        if (userCipherInfo != null) {
           // UserArchivesInfo userArchivesInfo = userArchivesInfoService.queryById(userCipherInfo.getUserId());
            // 密码正确
            if (userCipherInfo.getExpireNum() < 1) {
                return Result.failure(USERNAME_EXPIRE_NUM,USERNAME_EXPIRE_NUM_INFO); // 密码有效次数小于1
            }
            userCipherInfo.setExpireNum(userCipherInfo.getExpireNum()-1);
            userCipherInfo.setSsid(decodeToString(authorizationVo.getSsid()));
            UserDeviceInfo userDeviceInfo = new UserDeviceInfo();
            userDeviceInfo.setUserId(userCipherInfo.getUserId()); // 用户档案id
            userDeviceInfo.setPassword(authorizationVo.getPassword());
            List<UserDeviceInfo> userDeviceInfoList = userDeviceInfoService.queryAll(userDeviceInfo);// 查询符合用户的设备列表
            if (userDeviceInfoList.size() > 3) {
                return Result.failure(AUTHORIZATION_TERMINAL_NUM,AUTHORIZATION_TERMINAL_INFO); // 认证设备过多
            }
            if (userDeviceInfoList.size() > 0) {
                Result result = authMac(authorizationVo);
                if (result.getCode() == 200) {
                    insertUserDeviceInfo(userDeviceInfo, authorizationVo, userCipherInfo);// 新增设备信息
                    return result;
                }else {
                    return result;
                }
            }
            Result result = authMac(authorizationVo);
            if (result.getCode() == 200) {
                insertUserDeviceInfo(userDeviceInfo, authorizationVo, userCipherInfo);// 新增设备信息
            }
            return result;
        }
        return Result.failure(PARAMETER_NOT_FOUND,parameterNotFound); // 参数错误
    }

    private Result authMac(AuthorizationDto authorizationVo) throws Exception {
        if (authorizationVo.getPortalUserId() == null) {
            return Result.failure(PARAMETER_NOT_FOUND,parameterNotFound); // 参数错误
        }
        PortalUser portalUser = portalUserService.queryById(authorizationVo.getPortalUserId());
        String auth = auth(portalUser.getTenantName(), portalUser.getTenantPwd(), portalUser.getDomain(), authorizationVo);// 认证授权
        if (auth == null) {
            return Result.failure(AUTH_FLAIRE,AUTH_FLAIRE_DESC); //认证失败
        }
        insertAuthorizationUser(authorizationVo, IdUtil.getStringId(), auth); // 插入认证信息
        //记录上网日志
        insertUserLogInfo(new UserLogInfo(),authorizationVo);
        return Result.ok();
    }

    /**
     * 新增用户设备
     * @param userDeviceInfo
     * @param authorizationVo
     * @param userCipherInfo
     */
    private UserDeviceInfo insertUserDeviceInfo(UserDeviceInfo userDeviceInfo,AuthorizationDto authorizationVo,UserCipherInfo userCipherInfo) {
        userDeviceInfo.setDeviceMac(authorizationVo.getUmac());
        userDeviceInfo.setId(IdUtil.getStringId());
        userDeviceInfo.setExpireTime(getAddDate(userCipherInfo.getUpdateTime(),180));
        userDeviceInfo.setEffectTime(new Date());
        userDeviceInfoService.insert(userDeviceInfo);
        return userDeviceInfo;
    }

    /**
     * 无密码方案
     * @param authorizationVo
     * @return
     */
    private Result noPasswordAuth(AuthorizationDto authorizationVo) throws Exception {
        if (authorizationVo.getPortalUserId() == null) {
            return Result.failure(PARAMETER_NOT_FOUND,parameterNotFound); // 参数错误
        }
        log.info("getUmac：  "+JsonXMLUtils.obj2json(authorizationVo));
        PortalUser portalUser = portalUserService.queryById(authorizationVo.getPortalUserId());
        if (portalUser == null) {
            return Result.failure(AUTH_FLAIRE,AUTH_FLAIRE_DESC); //认证失败
        }
        String auth = auth(portalUser.getTenantName(), portalUser.getTenantPwd(), portalUser.getDomain(), authorizationVo);// 认证授权
        if (auth == null) {
            return Result.failure(AUTH_FLAIRE,AUTH_FLAIRE_DESC); //认证失败
        }
        insertAuthorizationUser(authorizationVo, IdUtil.getStringId(), auth); // 新增认证信息
        UserDeviceInfo userDeviceInfo = new UserDeviceInfo();
        userDeviceInfo.setId(IdUtil.getStringId());
        userDeviceInfo.setDeviceMac(authorizationVo.getUmac());
        userDeviceInfo.setEffectTime(new Date());
        userDeviceInfoService.insert(userDeviceInfo);
        //记录上网日志
        insertUserLogInfo(new UserLogInfo(),authorizationVo);
        return Result.ok();
    }

    /**
     * 新增用户上网记录
     * @param userLogInfo
     * @param authorizationVo
     */
    private void insertUserLogInfo(UserLogInfo userLogInfo,AuthorizationDto authorizationVo) {
        userLogInfo.setId(IdUtil.getStringId());
        userLogInfo.setApMac(authorizationVo.getApmac());
        userLogInfo.setDeviceIp(authorizationVo.getUaddress());
        userLogInfo.setDeviceMac(authorizationVo.getUmac());
        userLogInfo.setOnlineTime(new Date());
        userLogInfoService.insert(userLogInfo);
    }

    /**
     * 授权终端
     * @param username
     * @param password
     * @param domain
     * @param authorizationVo
     * @return
     * @throws Exception
     */
    private String auth(String username,String password,String domain,AuthorizationDto authorizationVo) throws Exception {
        HttpHeaders requestHeaders = getHttpHeaders();
        HttpHeaders token = getToken(username, password, domain, requestHeaders);
        if (token == null) {
            return null;
        }
        authorizationVo.setUserName(username);
        return setAuthorizationUser(token, domain, authorizationVo);
    }

    private HttpHeaders getHttpHeaders() {
        HttpHeaders requestHeaders = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        requestHeaders.setContentType(type);
        requestHeaders.add("Accept", MediaType.APPLICATION_JSON.toString());
        return requestHeaders;
    }

    /**
     * 用户获取账号和密码 有效期为5分钟
     * @param findAccountAndPasswordDto
     * @return
     * @throws Exception
     */
    @Override
    @Transactional
    public Result getUserNameAndPassword(FindAccountAndPasswordDto findAccountAndPasswordDto) throws Exception {
        if (redisUtil.redisLike(findAccountAndPasswordDto.getNumber()) == null) {
            return Result.failure(REPEAT_APPLY,REPEAT_APPLY_DESC); // 重复申请
        }
        ManageInfo manageInfo = userInfoService.queryBySecret(findAccountAndPasswordDto.getSecret());
        UserArchivesInfo userArchivesInfo = userArchivesInfoService.getUserNameAndPassword(manageInfo.getId(),findAccountAndPasswordDto.getNumber());
        if (userArchivesInfo != null) {
            UserCipherInfo result = getResult(userArchivesInfo, new UserCipherInfo());
            userCipherInfoService.insert(result);
            UserCipherInfoDto userCipherInfoDto = ModelMapperUtil.strictMap(result, UserCipherInfoDto.class);
            redisUtil.set(userCipherInfoDto.getPassword().
                    concat(":").concat(findAccountAndPasswordDto.getNumber()),1,JsonXMLUtils.obj2json(userCipherInfoDto),5); // 设置有效期 5分钟
            return Result.ok().setData(userCipherInfoDto);
        }
        userArchivesInfo = new UserArchivesInfo();
        userArchivesInfo.setId(IdUtil.getStringId());
        userArchivesInfo.setManageId(manageInfo.getId());
        userArchivesInfo.setType(findAccountAndPasswordDto.getType());
        userArchivesInfo.setNumber(findAccountAndPasswordDto.getNumber());
        userArchivesInfoService.insert(userArchivesInfo);
        UserCipherInfo result = getResult(userArchivesInfo, new UserCipherInfo());
        userCipherInfoService.insert(result);
        UserCipherInfoDto userCipherInfoDto = ModelMapperUtil.strictMap(result, UserCipherInfoDto.class);
        redisUtil.set(userCipherInfoDto.getPassword().
                concat(":").concat(findAccountAndPasswordDto.getNumber()),1,JsonXMLUtils.obj2json(userCipherInfoDto),5); // 设置有效期 5分钟
        return Result.ok().setData(ModelMapperUtil.strictMap(result, UserCipherInfoDto.class));
    }

    @Override
    public Result authorizationUserNoPassword(AuthorizationDto authorizationVo) throws Exception {
        Date date = new Date();
        if (authorizationVo.getProgrammeInfoId() != null && !"".equals(authorizationVo.getProgrammeInfoId()))  {
            ManageProgrammeInfo manageProgrammeInfo = manageProgrammeInfoService.queryById(authorizationVo.getProgrammeInfoId());
            DeviceUserView deviceUserView = new DeviceUserView();
            deviceUserView.setDeviceMac(authorizationVo.getUmac());
            deviceUserView.setMangeId(manageProgrammeInfo.getManageId());
            deviceUserView = deviceUserViewService.queryAll(deviceUserView);
            if (deviceUserView == null) {
                return Result.failure(DEVICE_Off_SITE_NOT_FOUND,DEVICE_Off_SITE_NOT_FOUND_DESC); //参数错误
            }
            if (deviceUserView.getExpireTime() != null) {
                if (deviceUserView.getExpireTime().getTime() > date.getTime()) {
                    //记录上网日志
                    insertUserLogInfo(new UserLogInfo(),authorizationVo);
                    return authMac(authorizationVo);
                }
            }
            return Result.failure(DEVICE_Off_SITE,EVICE_Off_SITE_DESC); //参数错误
        }
        return Result.failure(PARAMETER_NOT_FOUND,parameterNotFound); //参数错误
    }

    /**
     * 生成账号和密码
     * @param userArchivesInfo
     * @param userCipherInfo
     * @return
     */
    private  UserCipherInfo getResult(UserArchivesInfo userArchivesInfo,UserCipherInfo userCipherInfo){
        userCipherInfo.setId(IdUtil.getStringId());
        userCipherInfo.setUserId(userArchivesInfo.getId());
        userCipherInfo.setUserName(getRandomString(5));
        userCipherInfo.setPassword(getRandom(6));
        userCipherInfo.setStatus(true);
        userCipherInfo.setGrantDate(new Date());
        userCipherInfo.setUpdateTime(userCipherInfo.getGrantDate());
        userCipherInfo.setExpire("5");
        userCipherInfo.setNoSecret(false);
        userCipherInfo.setNoUserName(false);
        userCipherInfo.setUseNum(5);
        userCipherInfo.setExpireNum(5);
        return userCipherInfo;
    }
    /**
     * 递归查询缓存中存在验证码
     * @return
     */
    private  String getRandom(int i){
        String randomNum = CharUtil.getRandomNum(i);
        UserCipherInfo userCipherInfo = userCipherInfoService.queryByPassword(randomNum);
        if (userCipherInfo != null) {
            long datePoor = getDatePoor(new Date(), userCipherInfo.getGrantDate());
            if (datePoor > 5) {
                return randomNum;
            }else {
                getRandom(i);
            }
        }
        return randomNum;
    }
    /**
     * @param authorizationVo
     * @param id
     * @param response
     * @return
     */
    private AuthorizationUser insertAuthorizationUser(AuthorizationDto authorizationVo, String id, String response) {
        AuthorizationUser authorizationUser =new AuthorizationUser();
        authorizationUser.setId(id);
        authorizationUser.setApMac(authorizationVo.getApmac());
        authorizationUser.setNodeIp(authorizationVo.getNodeIp());
        authorizationUser.setPsessionId(response);
        authorizationUser.setSsid(authorizationVo.getSsid());
        authorizationUser.setStatus("0");
        authorizationUser.setTerminalIpV4(authorizationVo.getUaddress());
        authorizationUser.setTerminalMac(authorizationVo.getUmac());
        if (authorizationVo.getUserName() != null && !"".equals(authorizationVo.getUserName())) {
            authorizationUser.setUserName(authorizationVo.getUserName());
        }
        authorizationUser.setThirdAuthType(authorizationVo.getAuthType());
        authorizationUserDao.insert(authorizationUser);
        return authorizationUser;

    }

    /**
     * 授权终端用户
     * @param requestHeaders
     * @param authorizationVo
     * @return
     * @throws Exception
     */

    private String setAuthorizationUser(HttpHeaders requestHeaders,String domain, AuthorizationDto authorizationVo) throws Exception {
        try {
            Map map = new HashMap();
            Authorization authorizationUser = new Authorization();
            authorizationUser.setDeviceMac(authorizationVo.getApmac());
            authorizationUser.setSsid(authorizationVo.getSsid());
            authorizationUser.setTerminalIpV4(authorizationVo.getUaddress());
            if (authorizationVo.getNodeIp() != null) {
                authorizationUser.setNodeIp(authorizationVo.getNodeIp());
            }
            authorizationUser.setTerminalMac(authorizationVo.getUmac());
            if (authorizationVo.getUserName() != null && !"".equals(authorizationVo.getUserName())) {
                authorizationUser.setUserName(authorizationVo.getUserName());
            }
            map = JSONObject.parseObject(JsonXMLUtils.obj2json(authorizationUser));
            HttpEntity<Map> requestEntity = new HttpEntity<Map>(map, requestHeaders);
            ResponseEntity<JSONObject> response = restTemplate.postForEntity(URL_FIRST.concat(domain).concat(ApiUrlConstants.AUTHORIZATION_URL), requestEntity, JSONObject.class);
            JSONObject jsonObject = response.getBody();
            LOG.info("jsonObject : "+ jsonObject);
            return jsonObject.getString("psessionid");
        }catch (Exception e) {
            LOG.error(e.getMessage());
            return null;
        }
    }

    /**
     * 获取token_id
     * @param username
     * @param password
     * @param requestHeaders
     * @return
     */
    private HttpHeaders getToken(String username, String password,String domain, HttpHeaders requestHeaders) {
        String token = "";
        if (redisUtil.exist(username.concat("token"))) {
            token = redisUtil.getValue(username.concat("token"));
            requestHeaders.add("X-AUTH-TOKEN", token);
            requestHeaders.add("Accept-Language", "en-US");
            return requestHeaders ;
        } else {
            try {
                Map map = new HashMap();
                map.put("userName", username);
                map.put("password", password);
                HttpEntity<Map> requestEntity = new HttpEntity<Map>(map, requestHeaders);
                ResponseEntity<JSONObject> response = restTemplate.postForEntity(URL_FIRST.concat(domain).concat(ApiUrlConstants.CLOUD_CAMPUS_TOKEN_URL), requestEntity, JSONObject.class);
                JSONObject jsonObject = response.getBody().getJSONObject("data");
                token = jsonObject.get("token_id").toString();
                redisUtil.set(username.concat("token"),1,token,10);
                requestHeaders.add("X-AUTH-TOKEN",token);
                requestHeaders.add("Accept-Language", "en-US");
                return requestHeaders;
            }catch (Exception e) {
                return null;
            }
        }
    }
}
